// ~ Package Declaration
// ==================================================

package com.wms.data.domain.service.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.wms.data.PaginateModel;
import com.wms.data.dao.jdbc.AbstractJdbcDao;
import com.wms.data.domain.service.AbstractService;
import com.wms.data.domain.service.ServiceException;
import com.wms.data.pojo.Pojo;

// ~ Comments
// ==================================================

/**
 * common: service implementation class.
 * 
 * <table border="1" width="80%">
 * <tr>
 * <th>Version</th>
 * <th>Date</th>
 * <th>Role</th>
 * <th>Modified By</th>
 * <th>Details</th>
 * </tr>
 * <tr>
 * <td>%I%</td>
 * <td>2011/12/1</td>
 * <td>SD/PG</td>
 * <td>Lardy</td>
 * <td>Created By</td>
 * </tr>
 * </table>
 * <p/>
 * 
 * <pre>
 *	abstract implementation class. 
 * </pre>
 *
 * @author Lardy
 * @version 1.0
 * @since 1.5
 * 
 * SVN:
 * <ol>
 * 	<li>$Id$</li>
 * 	<li>$Rev$</li>
 * </ol>
 *
 * @param <DAO>
 * @param <T>
 * @param <PK>
 */
public class AbstractServcieImpl<DAO extends AbstractJdbcDao<T, PK>, T extends Pojo<PK>, PK extends Serializable>
		implements AbstractService<T, PK>, InitializingBean, ApplicationContextAware {

	// ~ Static Fields
	// ==================================================

	public static final String THE_CODE_ALREADY_USED = "PSTS.001";
	public static final String THE_CODE_AD_USER_MISSING = "PSTS.MSG.ADUSERMISSING";
	
	public static final String CREATE_SUCCESS_MSGCODE = "PSTS.007";
	public static final String UPDATE_SUCCESS_MSGCODE = "PSTS.009";
	public static final String DELETE_SUCCESS_MSGCODE = "PSTS.011";
	
	public static final String CREATE_FAILED_MSGCODE = "PSTS.008";
	public static final String UPDATE_FAILED_MSGCODE = "PSTS.010";
	public static final String DELETE_FAILED_MSGCODE = "PSTS.012";
	public static final String DELETE_FAILED_NOTDELETE = "PSTS.032";
	
	public static final String RECORD_EXPIRED = "PSTS.037";
	
	public final static String LOGIN_MSG_SUCCESS = "SUCCESS";
	public final static String LOGIN_MSG_FAILED= "FAILED";
	
	// ~ Fields
	// ==================================================
	
	private ApplicationContext applicationContext;
	
	public DAO dao;
	
	private Class<DAO> daoClass;
	
	private Class<T> pojoClass;
	
	private Class<PK> pkClass;
	
	protected Log logger = LogFactory.getLog(this.getClass());

	// ~ Constructors
	// ==================================================
	
	/**
	 * AbstractServcieImpl Constructors.
	 *
	 * <pre>
	 * 	2011/12/1
	 * </pre>
	 *
	 */
	@SuppressWarnings("unchecked")
	public AbstractServcieImpl() {

		Type[] actualTypeArguments = findParameterizedType(getClass()).getActualTypeArguments();

		if (actualTypeArguments.length < 3) {
			
			throw new IllegalArgumentException("Generic type dao, pojo, pk has be setted!");
		}
		
		this.daoClass = ((Class<DAO>) actualTypeArguments[0]);
		
		this.pojoClass = ((Class<T>) actualTypeArguments[1]);
		
		this.pkClass = ((Class<PK>) actualTypeArguments[2]);
	}

	public boolean findByColumn() {
		
		return true;
	}
	
	// ~ Methods
	// ==================================================

	/**
	 * Find T by Id.
	 *
	 * <pre>
	 *	2011/12/1
	 * </pre>
	 *
	 * @see com.psts.services.AbstractInterfaceService#findById(java.io.Serializable)
	 */
	public T findById(PK id) {

		return this.dao.findById(id);
	}

	/**
	 * Find all List<T> .
	 *
	 * <pre>
	 *	2011/12/1
	 * </pre>
	 *
	 * @see com.psts.services.AbstractInterfaceService#findAll()
	 */
	public List<T> findAll() {

		return this.dao.findAll();
	}
	
	/* (non-Javadoc)
	 * @see com.wms.data.domain.service.AbstractService#findAll(com.wms.data.PaginateModel)
	 */
	public List<T> findAll(PaginateModel paginateModel) {
		
		return this.dao.findAll(paginateModel);
	}
	
	public List<T> find(T t, PaginateModel paginateModel) {
		
		return this.dao.findByPojoFields(t, paginateModel);
	}
	
	public List<T> find(T t) {
		
		return this.dao.findByPojoFields(t);
	}

	public PK save(T t) {

		try {
			
			return this.dao.save(t);
		}
		catch (Exception e) {
		
			throw new ServiceException(CREATE_FAILED_MSGCODE);
		}
	}

	public void update(T t) {

		try {
			
			this.dao.update(t);
		}
		catch (Exception e) {
		
			throw new ServiceException(UPDATE_FAILED_MSGCODE);
		}
	}
	
	public void saveOrUpdate(T t) {
		
		try {
			
			if (t.getId() == null || t.getId().equals("")) {
				
				save(t);
			}
			else {
				
				update(t);
			}
		}
		catch (Exception e) {
		
			throw new ServiceException(UPDATE_FAILED_MSGCODE);
		}
	}

	public void delete(T t) {
		
		try {
			
			this.dao.delete(t);
		}
		catch (Exception e) {
		
			throw new ServiceException(DELETE_FAILED_MSGCODE);
		}
	}

	/**
	 * Set ApplicationContext.
	 *
	 * <pre>
	 *	2011/12/01
	 * </pre>
	 *
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
	 */
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

		this.applicationContext = applicationContext;		
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void afterPropertiesSet() throws Exception {

		Map<String, DAO> beans = this.applicationContext.getBeansOfType(getDaoClass());
		
		if (beans.size() > 1) {
			
			throw new NoSuchBeanDefinitionException(getDaoClass().getSimpleName());
		}

		this.dao = (DAO) ((AbstractJdbcDao) beans.values().iterator().next());
	}
	
	/**
	 * Find ParameterizedType by class.
	 *
	 * <pre>
	 * 	2011/12/01
	 * </pre>
	 *
	 * @param Class
	 * @return ParameterizedType
	 */
	@SuppressWarnings("rawtypes")
	private ParameterizedType findParameterizedType(Class clazz) {

		Type genericSuperclass = clazz.getGenericSuperclass();
		
		if (!(genericSuperclass instanceof ParameterizedType)) {
			
			return findParameterizedType(clazz.getSuperclass());
		}
		
		return (ParameterizedType) genericSuperclass;
	}
	
	/**
	 * @return the dao。
	 */
	public DAO getDao() {

		return dao;
	}

	/**
	 * @param dao set dao。
	 */
	public void setDao(DAO dao) {

		this.dao = dao;
	}

	/**
	 * @return the daoClass。
	 */
	public Class<DAO> getDaoClass() {

		return daoClass;
	}

	/**
	 * @param daoClass set daoClass。
	 */
	public void setDaoClass(Class<DAO> daoClass) {

		this.daoClass = daoClass;
	}

	/**
	 * @return the pojoClass。
	 */
	public Class<T> getPojoClass() {

		return pojoClass;
	}

	/**
	 * @param pojoClass set pojoClass。
	 */
	public void setPojoClass(Class<T> pojoClass) {

		this.pojoClass = pojoClass;
	}

	/**
	 * @return the pkClass。
	 */
	public Class<PK> getPkClass() {

		return pkClass;
	}

	/**
	 * @param pkClass set pkClass。
	 */
	public void setPkClass(Class<PK> pkClass) {

		this.pkClass = pkClass;
	}

	/**
	 * @return the applicationContext。
	 */
	public ApplicationContext getApplicationContext() {

		return applicationContext;
	}
}